##0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 options
# option(BUILD_SHARED_LIBS "Build shared libraries" ON)
option(my_ENABLE_CUDA_DEBUG "Enable cuda-gdb for builds" OFF)

##0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 Paths configs
set(my_ROOT_DIR "${CMAKE_SOURCE_DIR}" CACHE PATH "Root directory of this project")
message(STATUS "[my] Root directory of this project: ${my_ROOT_DIR}")
## my_UPPER_ROOT 是 ROOT dir 往上一层的目录; 通常是 git 仓库根目录
# get_filename_component(my_UPPER_ROOT ${my_ROOT_DIR} DIRECTORY CACHE "Totally git repo dir")
set(my_UPPER_ROOT ${my_ROOT_DIR} CACHE PATH "Upper root of this project")
message(STATUS "[my] Upper root of this project: ${my_UPPER_ROOT}")
##xxxxxxxxxxxxxxxxxxxxxx
set(my_COMMON_DIR "${my_UPPER_ROOT}/src/common" CACHE PATH "common include directory")
set(my_THIRD_PARTY_HXX "${my_UPPER_ROOT}/thirdpartyhxx" CACHE PATH "third party head-only libs") # header only 三方库
set(my_CONAN_RUNTIME_DIR "${CMAKE_BINARY_DIR}/runtime" CACHE PATH "Conan runtime collection directory")
message(STATUS "[my] Conan runtime collection directory: ${my_CONAN_RUNTIME_DIR}")
set(my_TESTIO_DIR "${my_UPPER_ROOT}/testio" CACHE PATH "test io files") # 测试输入输出文件夹
set(my_PATH_BIN "${my_ROOT_DIR}/bin" CACHE PATH "Binary output directory")
message(STATUS "[my] Binary output directory: ${my_PATH_BIN}")
##---- 检查是否为单配置生成器（如 Ninja）或多配置生成器（如 Visual Studio）
if(CMAKE_CONFIGURATION_TYPES)
    # 多配置生成器（如 Visual Studio）
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${my_PATH_BIN})
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${my_PATH_BIN})
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${my_PATH_BIN})
    message(STATUS "[my] Multi-config generator - Output directory: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
else()
    # 单配置生成器（如 Ninja）
    # if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Release")
    set(OUTPUT_SUBDIR ${CMAKE_BUILD_TYPE})
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${my_PATH_BIN}/${OUTPUT_SUBDIR}")
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${my_PATH_BIN}/${OUTPUT_SUBDIR}")
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${my_PATH_BIN}/${OUTPUT_SUBDIR}")
    message(STATUS "[my] Single-config generator - Output directory: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
    # endif()
endif()
##--------- 自行编译的第三方库目录
set(my_THIRD_PARTY "${my_ROOT_DIR}/thirdparty" CACHE PATH "Third party directory")
##--------- 日志的输出目录
set(my_LOG_DIR "${my_PATH_BIN}/logs" CACHE PATH "Logs directory")
##--------- dllexport header files dir
set(my_EXPORTS_DIR "${my_ROOT_DIR}/export_headers" CACHE PATH "Export headers directory")
##--------- cmake install dir
set(CMAKE_INSTALL_PREFIX "${my_ROOT_DIR}/inst-demo" CACHE PATH "Install path" FORCE) # 安装目录, demo
message(STATUS "[my] Install directory: ${CMAKE_INSTALL_PREFIX}")
##--------- 安装目录安排
set(my_INST_INCLUDE_DIR "include" CACHE PATH "Include directory of this library")
set(my_INST_EXE_DIR "bin" CACHE PATH "Executable directory of this library")
## windows下, dll 直接放到可执行文件目录
if(WIN32)
    set(my_INST_DLL_DIR ${my_INST_EXE_DIR} CACHE PATH "DLL directory of this library")
endif()
if(LINUX)
    set(my_INST_DLL_DIR "lib" CACHE PATH ".so directory of this library")
endif()

##0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 自定义函数
##============================ 拷贝到指定目录
function(my_cp_dlls_dest target dest_dir)
    if(NOT WIN32)
        message(STATUS "[my] Skip, not in windows")
        return()
    endif()
    add_custom_command(TARGET ${target} POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_RUNTIME_DLLS:${target}> ${dest_dir}
        COMMAND_EXPAND_LISTS
        COMMENT "Copying DLLs to build directory")
endfunction()

##============================ copy project dependent dlls to binary dir, windows
function(my_cp_dlls target)
    my_cp_dlls_dest(${target} $<TARGET_FILE_DIR:${target}>)
endfunction()


##============================ 配置 MSVC 的 RelWithDebInfo 模式, 添加更多debug选项
function(my_config_msvc_relwithdebinfo target_name)
    if(NOT TARGET ${target_name})
        message(WARNING "Target ${target_name} 不存在")
        return()
    endif()

    ## 确保只在 MSVC 编译器下执行
    set(my_REL_DEB_OPTS "/utf-8;/EHsc;/Zc:__cplusplus;/permissive-;/Od;/Zi;/DDEBUG;/UNDEBUG")
    list(JOIN my_REL_DEB_OPTS_CUDA "," my_REL_DEB_OPTS)
    # 针对 RelWithDebInfo 关闭优化，启用调试宏
    target_compile_options(${target_name} PRIVATE
        # c++ 选项: 禁用优化; 确保生成调试信息; 定义 DEBUG 宏; 取消 NDEBUG 宏
        "$<$<AND:$<CONFIG:RelWithDebInfo>,$<COMPILE_LANGUAGE:CXX>>:${my_REL_DEB_OPTS}>" # C++: 合并所有选项
        "$<$<AND:$<CONFIG:RelWithDebInfo>,$<COMPILE_LANGUAGE:CUDA>>:-Xcompiler=${my_REL_DEB_OPTS_CUDA}>" # CUDA: 合并所有选项
    )

    # 确保生成 PDB 文件
    target_link_options(${target_name} PRIVATE
        "$<$<CONFIG:RelWithDebInfo>:/DEBUG>" # 确保生成 PDB 文件
    )

    # 设置运行时库为 Debug 版本（可选，但可能影响 ABI）
    # 如需启用，取消下面的注释
    # set_property(TARGET ${target_name} PROPERTY
    #     MSVC_RUNTIME_LIBRARY "MultiThreadedDebug$<$<CONFIG:Debug>:Debug>"
    # )
endfunction()

###============================ 生成和配置, export header; 导出符号的头文件
include(GenerateExportHeader)
function(my_config_export_header target_name export_dir header_install)
    # 参数检查
    if(NOT TARGET ${target_name})
        message(WARNING "Target ${target_name} not exist")
        return()
    endif()

    # 确保导出目录存在
    file(MAKE_DIRECTORY ${export_dir})

    # 确保导出目录已设置
    if(NOT export_dir)
        set(export_dir "${CMAKE_BINARY_DIR}/export_headers")
        message(STATUS "Non-exist export directory, use default: ${export_dir}")
    endif()

    # 设置导出头文件路径; PARENT_SCOPE 为了方便调用者使用 header 的路径
    set(${target_name}_EXPORTS "${export_dir}/${target_name}_export.h" PARENT_SCOPE)

    # 生成导出头文件
    generate_export_header(${target_name} EXPORT_FILE_NAME "${export_dir}/${target_name}_export.h")

    # 设置目标的包含目录
    target_include_directories(${target_name} PUBLIC
        $<BUILD_INTERFACE:${export_dir}>
        $<INSTALL_INTERFACE:include>
    )

    ## 安装导出头文件
    if(header_install)
        install(FILES "${export_dir}/${target_name}_export.h" DESTINATION include)
    endif()

endfunction()

##0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 C++ CMake 跨平台通用设置, 需要放在 project(xxx) 调用后面
##--------- toolchain configs; IDE 设置
if(MSVC)
    set_property(GLOBAL PROPERTY USE_FOLDERS ON)
endif()
##--------- 让 generator 生成 clangd 需要的 build/compile_commands.json
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
##============================= c++, cuda 语言设置
set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries")
##---------
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_STANDARD 17) # C++ 标准
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# set(CMAKE_CXX_EXTENSIONS OFF)
##------------ linux 二进制 属性
# set(CMAKE_SKIP_RPATH TRUE) # 禁用自动RPATH
# set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
set(CMAKE_INSTALL_RPATH "$ORIGIN")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH OFF)

##============================= CUDA 全局设置
if(my_USE_CUDA)
    ##---------
    find_package(CUDAToolkit REQUIRED)
    ##---------
    set(CMAKE_CUDA_STANDARD 17) # cuda c++17
    set(CMAKE_CUDA_STANDARD_REQUIRED ON)
    ##--------- cuda architectures
    # set(CMAKE_CUDA_ARCHITECTURES 75 80 86 87 89 90 100 110 120) # nvidia cuda sample 的设置
    # set(CMAKE_CUDA_ARCHITECTURES 86 120)
    # set(CMAKE_CUDA_ARCHITECTURES native)
    set(CMAKE_CUDA_ARCHITECTURES 86)
    message(STATUS "[my] CMAKE_CUDA_ARCHITECTURES => ${CMAKE_CUDA_ARCHITECTURES}")
    ##---------- 设置 CUDA 运行时库为动态链接
    set(CMAKE_CUDA_RUNTIME_LIBRARY HYBRID)
    include_directories(${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
    ##----------- cuda 文件单独编译
    set(CUDA_SEPARABLE_COMPILATION ON)
    set(CUDA_RESOLVE_DEVICE_SYMBOLS ON)
    ##------------- cuda debug 设置
    if(my_ENABLE_CUDA_DEBUG)
        message(STATUS "[nvcc] cuda debug mode")
        set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -G")        # enable cuda-gdb (可能大大降低某些 targets 的性能)
    else()
        message(STATUS "[nvcc] cuda release mode")
        set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -lineinfo") # add line information to all builds for debug tools (exclusive to -G option)
    endif()
endif()

##0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 MSVC c++ 设置
set(my_CXX_CMPILE_OPTIONS "")
set(my_CUDA_CMPILE_OPTIONS "")
if(MSVC)
    message(STATUS ">>>[my] Setting MSVC cl, link")

    # 1. 强制所有 C/C++ 目标使用动态运行时
    # set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
    message(STATUS "[my] MSVC compiler: ${CMAKE_CXX_COMPILER}")
    ##  "/fp:fast"
    set(my_CXX_CMPL_OPTS_MSVC "/utf-8" "/EHsc" "/Zc:__cplusplus" "/permissive-")
    list(JOIN my_CXX_CMPL_OPTS_MSVC " " my_CXX_CMPILE_OPTIONS)
    # message(STATUS "[my] my_CXX_CMPILE_OPTIONS =>  ${my_CXX_CMPILE_OPTIONS}")

    list(JOIN my_CXX_CMPL_OPTS_MSVC "," my_CUDA_CMPILE_OPTIONS)
    set(my_CUDA_CMPILE_OPTIONS -Xcompiler="${my_CUDA_CMPILE_OPTIONS}")
    # message(STATUS "[my] my_CUDA_CMPILE_OPTIONS => ${my_CUDA_CMPILE_OPTIONS}")
endif()

## 可选的：为C++代码禁用特定警告
# add_compile_options("$<$<AND:$<CXX_COMPILER_ID:MSVC>,$<COMPILE_LANGUAGE:CXX>>:/wd4819>")
# get_property(my_DBG_DIR_COMP_OPTS DIRECTORY ${my_ROOT_DIR} PROPERTY COMPILE_OPTIONS)
# message(STATUS "[my] my_DBG_DIR_COMP_OPTS => ${my_DBG_DIR_COMP_OPTS}")

##0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 Linux GCC 编译选项
set(my_COMPILER_ID "${CMAKE_CXX_COMPILER_ID}")
if(NOT CMAKE_CXX_COMPILER_ID)
    set(my_COMPILER_ID "${CMAKE_C_COMPILER_ID}")
endif()

set(my_CXX_CMPILE_OPTIONS "")
if(my_COMPILER_ID STREQUAL "GNU")
    message(STATUS "[my] GNU compiler: ${CMAKE_CXX_COMPILER}")
    # -fvisibility=hidden 默认让符号不可见
    # Set default visibility of all symbols to hidden
    set(CMAKE_CXX_VISIBILITY_PRESET "hidden")
    set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE)
    ## -Wno-pedantic 禁用pedantic模式下的扩展警告
    ## -Wextra 额外严格警告
    set(my_CXX_CMPL_OPTS_GCC -finput-charset=utf-8 -fexec-charset=utf-8 -Wall -Wextra -Wno-pedantic)
    list(JOIN my_CXX_CMPL_OPTS_GCC " " my_CXX_CMPILE_OPTIONS)

    list(JOIN my_CXX_CMPL_OPTS_GCC "," my_CUDA_CMPILE_OPTIONS)
    set(my_CUDA_CMPILE_OPTIONS "-Xcompiler=${my_CUDA_CMPILE_OPTIONS}")
    # message(STATUS "[my] my_CUDA_CMPILE_OPTIONS => ${my_CUDA_CMPILE_OPTIONS}")
endif()
##===================== 告诉 NVCC 不要狗叫
if(my_USE_CUDA AND CMAKE_CUDA_COMPILER AND TRUE)
    set(CUDA_WARNING_SUPPRESSIONS
        -diag-suppress=27   # 抑制fmt中的字符值超出范围警告
        -diag-suppress=128  # 抑制fmt中的"loop is not reachable"警告
        -diag-suppress=2417 # 抑制fmt中的constexpr构造函数警告
        -diag-suppress=1388 # 抑制spdlog中的DLL接口规范不一致警告
        -diag-suppress=1394 # 抑制spdlog中的DLL接口字段警告
    )
    list(JOIN CUDA_WARNING_SUPPRESSIONS " " CUDA_WARNING_SUPPRESSIONS)
    set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${CUDA_WARNING_SUPPRESSIONS}")
endif()
##===================== apply flags
#---- https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS.html
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_CXX_CMPILE_OPTIONS}")
message(STATUS "[my] CMAKE_CXX_FLAGS => ${CMAKE_CXX_FLAGS}")
#----
if(my_USE_CUDA)
    set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${my_CUDA_CMPILE_OPTIONS}")
    message(STATUS "[nvcc] CMAKE_CUDA_FLAGS => ${CMAKE_CUDA_FLAGS}")
endif()

##0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 find packages
##xxxxxxxxxxxxxxxxxxxxxxxxxxxxx fmt
# 设置FMT_UNICODE=0禁用Unicode支持，避免fmt库中的static_assert错误
# 或者设置为1并确保所有编译器都使用/utf-8选项
#add_compile_definitions("FMT_UNICODE=0")
#find_package(fmt CONFIG REQUIRED)

##xxxxxxxxxxxxxxxxxxxxxxxxxxxxx spdlog
#find_package(spdlog CONFIG REQUIRED)

##xxxxxxxxxxxxxxxxxxxxxxxxxxxxx python interpreter
find_package(Python COMPONENTS Interpreter Development)
message(STATUS "[python] Python_EXECUTABLE => ${Python_EXECUTABLE}")
message(STATUS "[python] Python_COMPILER_ID => ${Python_COMPILER_ID}")
message(STATUS "[python] Python_VERSION => ${Python_VERSION}")

##xxxxxxxxxxxxxxxxxxxxxxxxxxxxx pytorch
##torch cmake folder
# 查找Torch安装目录的CMake代码
# 支持Windows和Linux平台
# 初始化Torch安装目录变量
set(my_TORCH_PKG_DIR "")
# 根据不同平台设置候选路径
if(WIN32)
## Windows平台候选路径
    set(torch_candidate_paths
        "C:/cpplibs/libtorch"
        "$ENV{LOCALAPPDATA}/Programs/Python/Python312/Lib/site-packages/torch"
        # 增加Python常见版本路径
        "$ENV{LOCALAPPDATA}/Programs/Python/Python311/Lib/site-packages/torch"
    )
elseif(UNIX AND NOT APPLE)
## Linux平台候选路径
    set(torch_candidate_paths
        "/usr/local/lib/python3.12/dist-packages/torch"
        "/usr/lib/python3/dist-packages/torch"
        "$ENV{HOME}/.local/lib/python3.12/site-packages/torch"
        "/usr/local/lib/python3.11/dist-packages/torch"
        "/usr/lib/python3.11/dist-packages/torch"
        "$ENV{HOME}/.local/lib/python3.11/site-packages/torch"
    )
endif()
## 检查每个候选路径
foreach(path IN LISTS torch_candidate_paths)
    if(EXISTS "${path}")
        set(my_TORCH_PKG_DIR "${path}")
        message(STATUS "Torch package directory found: ${my_TORCH_PKG_DIR}")
        break()
    endif()
endforeach()

## 如果未找到，给出提示
if(NOT my_TORCH_PKG_DIR)
    message(FATAL_ERROR "Torch CMake directory missing, please ensure PyTorch installed")
endif()

message(STATUS "[torch] CUDA_nvrtc_LIBRARY => ${CUDA_nvrtc_LIBRARY}")
# set(TORCH_CUDA_ARCH_LIST "8.6")
list(PREPEND CMAKE_PREFIX_PATH "${my_TORCH_PKG_DIR}/share/cmake")

##xxxxxxxxxxxxxxxxxxxxxxxxxxxxx pybind11
if(TRUE)
    find_package(pybind11 REQUIRED)
    ##
    if(NOT pybind11_FOUND)
        message(WARNING "pybind11 not found, can't build python binding")
        return()
    endif()
endif()

##xxxxxxxxxxxxxxxxxxxxxxxxxxxxx pytorch
find_package(Torch REQUIRED)
message(STATUS "[torch]TORCH_INCLUDE_DIRS => ${TORCH_INCLUDE_DIRS}")
message(STATUS "[torch]TORCH_LIBRARIES => ${TORCH_LIBRARIES}")
message(STATUS "[torch]TORCH_CXX_FLAGS => ${TORCH_CXX_FLAGS}")
message(STATUS "[torch]Caffe2_MAIN_LIBS => ${Caffe2_MAIN_LIBS}")
message(STATUS "[torch]CAFFE2_INCLUDE_DIRS => ${CAFFE2_INCLUDE_DIRS}")
message(STATUS "[torch]ATEN_INCLUDE_DIR => ${ATEN_INCLUDE_DIR}")
message(STATUS "[torch]ATEN_LIBRARIES => ${ATEN_LIBRARIES}")

get_target_property(tmpl torch INTERFACE_LINK_LIBRARIES)
message(STATUS "[torch] torch.INTERFACE_LINK_LIBRARIES ->${tmpl}")

get_target_property(tmpl torch_cpu_library INTERFACE_LINK_LIBRARIES)
message(STATUS "[torch] torch_cpu_library.INTERFACE_LINK_LIBRARIES ->${tmpl}")

get_target_property(tmpl torch_cuda_library INTERFACE_LINK_LIBRARIES)
message(STATUS "[torch] torch_cuda_library.INTERFACE_LINK_LIBRARIES ->${tmpl}")

##xxxxxxxxxxxxxxxxxxxxxxxxxxxxx pytorch torch_python.lib, 可用于 pybind11 绑定
find_library(my_3RDLIB_TORCH_PYTHON
    torch_python NAMES_PER_DIR
    PATHS "${my_TORCH_PKG_DIR}/lib"
)
message(STATUS "[torch] my_3RDLIB_TORCH_PYTHON -> ${my_3RDLIB_TORCH_PYTHON}")